由於商品的物件都寫好了!
所以我們就來重構物車吧
今天也是直接上程式碼 ...
tests/CartTest.php
namespace Recca0120\Cart\Tests;
use Recca0120\Cart\Cart;
use Recca0120\Cart\Item;
use PHPUnit\Framework\TestCase;
class CartTest extends TestCase
{
/** @test */
public function 將商品加入購物車並驗證商品有名稱單價數量()
{
$item = $this->createItem('商品01', 100, 1);
$cart = new Cart();
$cart->put($item);
$this->assertArraySubset([$item], $cart->items());
}
/** @test */
public function 加總()
{
$cart = new Cart();
$cart->put($this->createItem('商品01', 100, 2));
$cart->put($this->createItem('商品02', 200, 1));
$this->assertEquals(400, $cart->total());
}
private function createItem($name, $price, $qty)
{
return new Item([
'name' => $name,
'price' => $price,
'quantity' => $qty,
]);
}
}
// src/Cart.php
namespace Recca0120\Cart;
class Cart
{
private $items = [];
public function put($item)
{
array_push($this->items, $item);
return $this;
}
public function items()
{
return $this->items;
}
public function total()
{
return array_sum(array_map(function ($item) {
return $item->getPrice() * $item->getQuantity();
}, $this->items));
}
}
這樣就完成了程式的重構
不過今天要講的不是那麼單純的程式改版
在 PHP 裡有個 ArrayAccess interface 只要 implements 它,
就可以讓物件操作起來像 Array
所以只要實作它,其實Cart的程式其實連程式碼都不必修改 ...
所以先來進一步修改商品物件吧
// tests/Item.php
namespace Recca0120\Cart;
use ArrayAccess;
class Item implements ArrayAccess
{
private $attributes = [];
public function __construct($attributes = [])
{
$this->attributes = $attributes;
}
public function setName($name)
{
$this->attributes['name'] = $name;
return $this;
}
public function getName()
{
return $this->attributes['name'];
}
public function setPrice($price)
{
$this->attributes['price'] = $price;
return $this;
}
public function getPrice()
{
return $this->attributes['price'];
}
public function setQuantity($quantity)
{
$this->attributes['quantity'] = $quantity;
return $this;
}
public function getQuantity()
{
return $this->attributes['quantity'];
}
public function toArray()
{
return $this->attributes;
}
public function offsetExists($offset)
{
return isset($this->attributes[$offset]);
}
public function offsetGet($offset)
{
return $this->attributes[$offset];
}
public function offsetSet($offset, $value)
{
$this->attributes[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->attributes[$offset]);
}
}
// tests/ItemTest.php
namespace Recca0120\Cart\Tests;
use Recca0120\Cart\Item;
use PHPUnit\Framework\TestCase;
class ItemTest extends TestCase
{
/** @test */
public function 測試商品屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item($attributes);
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 使用mehtod來設定屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item();
$item->setName($attributes['name']);
$this->assertSame($attributes['name'], $item->getName());
$item->setPrice($attributes['price']);
$this->assertSame($attributes['price'], $item->getPrice());
$item->setQuantity($attributes['quantity']);
$this->assertSame($attributes['quantity'], $item->getQuantity());
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 測試ArrayAccess()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item();
$item['name'] = $attributes['name'];
$this->assertSame($attributes['name'], $item['name']);
$item['price'] = $attributes['price'];
$this->assertSame($attributes['price'], $item['price']);
$item['quantity'] = $attributes['quantity'];
$this->assertSame($attributes['quantity'], $attributes['quantity']);
$this->assertEquals($attributes, $item->toArray());
}
}
改造完畢,
這樣就可以不用更改 Cart 的程式
並且可以讓 Item 有更多的操作方式
寫到這邊就能發現一件事情
寫測試想要寫的輕鬆,
其中一個要件就是對程式本身的熟悉度夠不夠高
熟悉度越高,寫起來相對輕鬆
熟悉度不高,就是改的地方多那麼一點點而已